package org.yunai.swjg.server.core;

import org.apache.commons.lang.exception.ExceptionUtils;
import org.slf4j.Logger;
import org.yunai.swjg.server.core.constants.SysMessageConstants;
import org.yunai.swjg.server.core.heartbeat.HeartbeatThread;
import org.yunai.swjg.server.core.service.GameExecutorService;
import org.yunai.swjg.server.core.service.GameMessageProcessor;
import org.yunai.swjg.server.core.service.Online;
import org.yunai.swjg.server.core.service.OnlineContextService;
import org.yunai.swjg.server.module.player.PlayerService;
import org.yunai.swjg.server.rpc.message.S_C.S_C_SysMessageReq;
import org.yunai.yfserver.common.LoggerFactory;
import org.yunai.yfserver.spring.BeanManager;

import java.util.Collection;

/**
 * 游戏服务器关闭钩子
 * User: yunai
 * Date: 13-4-27
 * Time: 下午10:19
 */
public class GameServerShutdownHooker implements Runnable {

    private static final Logger LOGGER = LoggerFactory.getLogger(LoggerFactory.Logger.server, GameServer.class);

    private static GameMessageProcessor gameMessageProcessor;
    private static HeartbeatThread heartbeatThread;
    private static OnlineContextService onlineContextService;
    private static PlayerService playerService;
    private static GameServer gameServer;
    private static GameExecutorService gameExecutorService;
    static {
        gameMessageProcessor = BeanManager.getBean(GameMessageProcessor.class);
        heartbeatThread = BeanManager.getBean(HeartbeatThread.class);
        onlineContextService = BeanManager.getBean(OnlineContextService.class);
        playerService = BeanManager.getBean(PlayerService.class);
        gameServer = BeanManager.getBean(GameServer.class);
        gameExecutorService = BeanManager.getBean(GameExecutorService.class);
    }

    /**
     * jvm关闭操作.<br />
     * <pre>
     *     1. 广播玩家.
     *     2. 设置服务器状态为不再开放.
     *        * 此时GameMessage类型消息不放到消息队列, 但是消息队列里的消息还依然处理
     *     3. 设置服务器状态关闭（会广播给World Server）
     *        TODO 暂时不考虑实现
     *     4. 停止HeartbeatThread.
     *        * 场景消息队列不再处理
     *        * 场景数据不再同步
     *        * 玩家消息队列不再处理(未处理完的消息就不再处理了)
     *        * 玩家数据不再同步
     *     5. 停止主消息队列
     *        * 未处理完的消息就不再处理了
     *     6. 关闭系统维护线程池服务
     *        * TODO 该服务目前基本没作用，等以后加
     *     7. 踢掉所有玩家
     *     8. 关闭Mina服务器
     *        * 该操作会立即强制关闭所有session
     *     9. 强制最后同步次DataUpdater
     *        * TODO 暂时认为该操作基本没用处，仅仅输出下错误日志
     * </pre>
     */
    @Override
    public void run() {
        LOGGER.info("[run] [GameServerShutdownHooker run begin].");

        // 1. 广播玩家
        LOGGER.info("[run] [通知所有玩家关服消息开始].");
        onlineContextService.broadcastMessage(new S_C_SysMessageReq(SysMessageConstants.SERVER_WILL_SHUTDOWN));
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            LOGGER.error("[run] [线程休眠来等待发送通知关服消息被打断] [error:{}]", ExceptionUtils.getStackTrace(e));
        }
        LOGGER.info("[run] [通知所有玩家关服消息成功].");

        // 2. 设置服务器状态为不再开放
        LOGGER.info("[run] [设置服务器状态为不开放开始].");
        GameServerRuntime.shutdown();
        LOGGER.info("[run] [设置服务器状态为不开放成功].");

        // 3. TODO 设置服务器状态关闭（会广播给World Server） 暂时不考虑实现

        // 4. 关闭心跳Thread
        LOGGER.info("[run] [停止心跳线程开始].");
        heartbeatThread.shutdown();
        LOGGER.info("[run] [停止心跳线程失败].");

        // 5. 停止主消息队列
        LOGGER.info("[run] [停止消息队列开始].");
        gameMessageProcessor.stop();
        LOGGER.info("[run] [停止消息队列成功].");

        // 6. 关闭系统维护线程池服务
        LOGGER.info("[run] [关闭系统维护线程池服务开始].");
        gameExecutorService.stop();
        LOGGER.info("[run] [关闭系统维护线程池服务成功].");

        // 7. 踢掉所有玩家
        LOGGER.info("[run] [让所有玩家下线开始].");
        playerService.offAllPlayersLogoutWhenServerShutDown();
        LOGGER.info("[run] [让所有玩家下线成功].");

        // 8. 关闭Mina服务器
        LOGGER.info("[run] [关闭Mina服务器开始].");
        gameServer.stop();
        LOGGER.info("[run] [关闭Mina服务器成功].");

        // 9. 强制最后同步次DataUpdater
        syncPlayerDataUpdater();

        LOGGER.info("[run] [GameServerShutdownHooker run success].");
    }

    /**
     * 强制最后同步次在线玩家<br />
     * TEST 目前感觉没用，就暂时输出下错误日志
     */
    private void syncPlayerDataUpdater() {
        Collection<Online> onlines = onlineContextService.getPlayers();
        if (onlines.size() > 0) {
            for (Online online : onlines) {
                if (online == null) {
                    continue;
                }
                LOGGER.error("[syncPlayerDataUpdater] [关闭服务器发现还有剩余在线玩家:{}].", online.getPlayer().getId());
            }
        }
    }
}
